<?php

namespace App\Admin\Service\Base;

use App\Common\Model\Base\Role;
use App\Common\Model\Base\Route;
use App\Common\Util\AdminAuth;
use App\Common\Util\TP;
use Psr\Http\Message\UriInterface;
use Sc\Util\ImitateAopProxy\AopProxyTrait;
use Sc\Util\Tool\Url;

class RolePermissionsService
{
    use AopProxyTrait;

    /**
     * 路由与ID的映射
     *
     * @var array<array>
     */
    public static array $routeIdMapping = [];

    /**
     * 角色的路由ID映射
     *
     * @var array<array>
     */
    public static array $roleRouteIds = [];


    public function init(): void
    {
        $this->cacheRouteMapping();
        $this->cacheRoleRouteIds();
    }

    /**
     * 缓存权限数据
     *
     * @return void
     * @throws \Throwable
     */
    public function cache(): void
    {
        $this->cacheRouteMapping(true);
        $this->cacheRoleRouteIds(true);
    }

    public function cacheRouteMapping(bool $toCache = false): void
    {
        $key = 'RouteMapping';
        if ($toCache) {
            TP::cache()->delete($key);
        }

        self::$routeIdMapping = TP::cache()->remember($key, function (){
            $routes = Route::field(['route', 'id'])->select();
            $newRoute = [];
            foreach ($routes as $route) {
                if (empty($newRoute[$route['route']])) {
                    $newRoute[$route['route']] = [
                        'route' => $route['route'],
                        'ids' => [
                            $route['id']
                        ]
                    ];
                    continue;
                }
                $newRoute[$route['route']]['ids'][] = $route['id'];
            }
            return $newRoute;
        });
    }

    /**
     * @param bool $toCache
     *
     * @return void
     * @throws \Throwable
     */
    public function cacheRoleRouteIds(bool $toCache = false): void
    {
        $key = 'RoleRouteIds';
        if ($toCache) {
            TP::cache()->delete($key);
        }

        self::$roleRouteIds = TP::cache()->remember($key, function (){
            return array_map(fn($v) => $v ? json_decode($v, true) : [], Role::column('route_ids', 'id'));
        });
    }

    /**
     * 检测
     *
     * @param Url|string $uri
     * @param array               $roles
     *
     * @return bool
     */
    public function check(Url|string $uri, array $roles): bool
    {
        if (is_string($uri)) {
            $path = $ac = $uri;
        }else{
            $path = $uri->getPath();
            $ac   = $uri->getQuery() ? $path . '?' . $uri->getQuery() : $path;
        }

        $appName = array_search(app('http')->getName(), TP::config('app.app_map')) ?: app('http')->getName();
        $route   = preg_replace("/^\/?$appName\//", "", $path);
        if (!is_string($uri)) {
            $ac   = $uri->getQuery() ? $route . '?' . $uri->getQuery() : $route;
        }

        if ((!isset(self::$routeIdMapping[$route])
            && !isset(self::$routeIdMapping[$ac]))
            || AdminAuth::isSuper()) {
            return true;
        }

        $routeIds = array_merge(self::$routeIdMapping[$route]['ids'] ?? [], self::$routeIdMapping[$ac]['ids'] ?? []);

        foreach ($roles as $role) {
            if (empty(self::$roleRouteIds[$role])) {
                continue;
            }
            if (array_intersect($routeIds, self::$roleRouteIds[$role])) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param array $roleIds
     *
     * @return array
     */
    public function getRouteIdsByRoleIds(array $roleIds): array
    {
        $routeIds = [];
        foreach ($roleIds as $roleId) {
            $routeIds = [...$routeIds, ...self::$roleRouteIds[$roleId] ?? []];
        }

        return $routeIds;
    }
}